#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<errno.h>
#include<netinet/in.h>
#include<ctype.h>
#include<time.h>
#include<getopt.h>
#include<netinet/in.h>
#include<pthread.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<libgen.h>
#include<ctype.h>
#define LISTEN_PORT	9999
#define BACKLOG		13
#define ARRAY_SIZE(x)	(sizeof(x)/sizeof(x[0]))


static inline void printf_usage(char *progname)
{
	printf("Usage: %s [OPTION]...\n",progname);
	printf("--d[daemon] set program running on background...\n");
	printf("--p[port] set server port...\n");
	printf("--h[help] displey this help information\n");
}
static inline void msleep(unsigned long ms);
int socket_server_init(char *listen_ip,int listen_port);

int main(int argc,char **argv)
{
	int 			listen_fd=-1;
	int 			connfd=-1;
	int 			rv=-1;
	int			opt=-1;
	char			*progname;
	struct sockaddr_in	serv_addr;
	struct sockaddr_in	cli_addr;
	socklen_t		cliaddr_len;
	char			buf[1024];
	int			fds_array[1024];
	int			maxfd=-1;
	int 			daemon_run=0;
	int			listenfd_connfd;
	fd_set			rdset;
	int			found=-1;
	int 			i=0,j=0;
	int			serv_port=-1;

	struct	option		long_options[]={
	{"port",required_argument,NULL,'p'},
	{"help",no_argument,NULL,'h'},
	{"daemon",no_argument,NULL,'d'},
	{NULL,0,NULL,0}
	};

	progname=basename(argv[0]);

	while((opt=getopt_long(argc,argv,"bp:h",long_options,NULL))!=-1)
	{
		switch(opt)
		{
			case 'h':
				printf_usage(argv[0]);
				break;
			case 'p':
				serv_port=atoi(optarg);
				break;

			case 'b':
				daemon_run=1;
				break;

			default:
				break;
		}

	}

	if(!serv_port)
	{
		printf_usage(argv[0]);
		return -1;
	}

	//创建listen_fd
	if((listen_fd=socket_server_init(NULL,serv_port))<0)
	{
		printf("ERROR: %s server listen on port %d failure\n",argv[0],serv_port);
		return -2;
	}
	printf("%s server start to listen on port %d \n",argv[0],serv_port);

	//将进程放入后台
	if(daemon_run)
	{
		daemon(0,0);
	}

	//将set初始化
	for(i=0;i<ARRAY_SIZE(fds_array);i++)
	{
		fds_array[i]=-1;
	}
	fds_array[0]=listen_fd;
	
	//相当于while 一直死循环
	
	while(1)
	{
		FD_ZERO(&rdset);
		for(i=0;i<ARRAY_SIZE(fds_array);i++)
		{
			//找不到
			if(fds_array[i]<0)
				continue;
		
			maxfd=fds_array[i]>maxfd ? fds_array[i] :maxfd;
			FD_SET(fds_array[i],&rdset);
		}

		//程序再这会在阻塞
		rv=select(maxfd+1,&rdset,NULL,NULL,NULL);
		if(rv<0)
		{
			printf("Select failure: %s\n",strerror(errno));
			break;
		}else if(rv==0)
		{
			printf("Select get timeout.\n");
			break;
		}

		//如果listen_fd 已经在rdset
		if(FD_ISSET(listen_fd,&rdset))
		{
			//如果listenfd 已经在rdset里面了
			connfd=accept(listen_fd,(struct sockaddr *)NULL,NULL);
			if(connfd<0)
			{
				printf("Accept new client failure:%s\n",strerror(errno));
				close(listen_fd);
			}

			found=0;
			//接收新的客户端
			for(i=0;i<ARRAY_SIZE(fds_array);i++)
			{
				if(fds_array[i]<0)
				{
					printf("Accept new client[%d] and add it into array\n",connfd);
					fds_array[i]=connfd;
					found=1;
					break;
				}
			}

			//已满的情况
			if(!found)
			{
				printf("Accept new client[%d],but full ,so ,refuse\n",connfd);
				close(connfd);
			}
		}
		else
		{	//如果说listenfd没有在rdset内
			
			
			for(int i=0;i<ARRAY_SIZE(fds_array);i++)
			{
				//如果没有找到connfd就一直阻塞，找到了就读
				if(fds_array[i]<0||!FD_ISSET(fds_array[i],&rdset))
				continue;
				
				rv=read(fds_array[i],buf,sizeof(buf));
				if(rv<0)
				{
					printf("Socekt[%d] read failure or get disconnected!\n",fds_array[i]);
					close(fds_array[i]);
					fds_array[i]=-1;
					close(fds_array[i]);
					continue;
				}	
				else if(0==rv)
				{
					printf("Get disconnected!\n");
					close(fds_array[i]);
				}
				else
				{
					printf("Read data %d bytes : %s from sock[%d]!\n",rv,buf,fds_array[i]);
					for(int j=0;j<rv;j++)
						buf[j]=toupper(buf[j]);

					if(write(fds_array[i],buf,rv)<0)
					{
						printf("Write back to client socket[%d] failure:%s!\n",fds_array[i],strerror(errno));
						close(fds_array[i]);
						fds_array[i]=-1;
						close(fds_array[i]);
					}
					printf("Write to client socket[%d] successfully!\n",fds_array[i]);
					
				}//else
			
			}//for
		}//else


	}
}

static inline void msleep(unsigned long ms)
{
	struct timeval 	tv;

	tv.tv_sec=ms/1000;
	tv.tv_usec=(ms/1000)*1000;

	select(0,NULL,NULL,NULL,&tv);
}

int socket_server_init(char *listen_ip,int listen_port)
{
	struct sockaddr_in	serv_addr;
	int			on=1;
	int			rv=1;
	int 			listen_fd;
//服务器端 首先创建socket  ipv4 tcp 0
	listen_fd=socket(AF_INET,SOCK_STREAM,0);
	if(listen_fd<0)
	{
		printf("Create a socket failure: %s!\n",strerror(errno));
		rv -1;
	}
	printf("Create a socket[%d] successfully!\n",listen_fd);

	//第二步，重置服务器端的数据 为什么这里求大小用的是指针呢？
	memset(&serv_addr,0,sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_port=htons(LISTEN_PORT);
	if(!listen_ip)
	{
		serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	}else
	{
		printf("NO specify ip\n");
	}
	//第三步步 bind
	if(bind(listen_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0)
	{
		printf("Use bind[%d] to bind the TCP socket  failure: %s!\n",listen_fd,strerror(errno));
		rv -2;
		goto  CleanUp;
	}
	//第四步 服务器监听
	if(listen(listen_fd,BACKLOG)<0)
	{
		printf("Use bind() to bind the TCP socket failure: %s\n",strerror(errno));
		rv -3;
		goto CleanUp;
	}

CleanUp:
	if(rv<0)
		close(listen_fd);
	else
		rv=listen_fd;
	return rv;

}

